<template>
  <div>AComponent</div>
  <p ref="ppp">{{ count }}</p>
  <p>{{ text }}</p>
  <button @click="handleClick">更新</button>

  用户名:<input type="text" v-model="userFormData.username" /> 密码:<input type="password" v-model="userFormData.password" />
  <p>{{ userFormData.age }}</p>
  <button @click="setAge">更新</button>
  <p>{{ sex }}</p>
</template>

<script lang="ts">
import {
  defineComponent,
  ref,
  reactive,
  readonly,
  onBeforeMount,
  onMounted,
  onBeforeUpdate,
  onUpdated,
  onBeforeUnmount,
  onUnmounted,
  computed,
  watch,
} from "vue";

/*
  组合式: 所有组合式Api都需要引入才能使用
  
    1. 定义响应式数据
      ref 定义单个响应式数据
        定义的响应式数据，如果在模板页面使用，可以直接用
        如果在setup中使用，必须使用 xxx.value
      reactive 定义一组响应式数据

      如果定义表单项数据，通常使用reactive，其他数据使用ref

    2. 使用生命周期函数
      setup函数本身，相当于beforeCreate, created

    3. 计算属性
      只读计算属性
      computed(() => {})
      可读可写计算属性
      computed({
        get() {},
        set() {}
      })
    
    4. 监视属性
      watch(xxx, (newVal, oldVal) => {}, { immediate: true, deep: true })
      watch(() => xxx.yyy, (newVal, oldVal) => {}, { immediate: true, deep: true })

    5. props
      - 父组件传递数据
        <Child :xxx="xxx" />

      - 子组件声明接受
        props: { xxx: Number }

      - 子组件使用
        - 模板页面，直接使用
        - setup函数中，通过第一个参数接受到props使用
          setup(props) { props.xxx }

    6. 自定义事件
      - 父组件绑定事件
        <Child @xxx="xxx" />
      
       - 子组件声明接受
        emits: ['xxx']
      
      - 子组件触发事件
        setup(props, context) { context.emit('xxx') }

    7. ref获取DOM元素或组件实例对象
      - 给元素绑定ref属性
        <Child ref="xxx" />
      - 定义ref数据
        const xxx = ref(); // 注意：数据名和上面绑定ref属性的值名称必须一直
      - 暴露ref数据
        return { xxx }
      - 使用
        xxx.value
*/
// 类型定义
interface CategoryItem {
  categoryId: number;
  categoryName: string;
  categoryChild: CategoryList;
}
type CategoryList = CategoryItem[];

export default defineComponent({
  name: "AComponent",
  props: {
    sex: {
      type: String,
      required: true,
    },
  },
  emits: ["say"],
  components: {},
  setup(props, context) {
    /*
      props就是props数据
      context是一个对象
        attrs 包含所有其他未声明接受的属性
        emit 触发自定义的事件
    */
    console.log(props, context);
    console.log(this); // undefined
    console.log(props.sex, context.attrs.address);

    // 触发自定义事件
    context.emit("say");

    // 1. 定义响应式数据
    const person = ref({
      // 引用数据类型
      name: "jack",
      age: 18,
    });

    /************************ userFormData功能开始 ************************/
    const userFormData = reactive({
      username: "",
      password: "",
      name: "jack",
      age: 18,
    });

    const setAge = () => {
      userFormData.age++;
    };

    // 监视属性
    watch(
      // 如果要监视reactive中某个数据，必须写成函数形式
      () => userFormData.age,
      (newVal, oldVal) => {
        console.log("age数据变化了", newVal, oldVal);
      },
      {
        immediate: true, // 是否第一次触发
        // deep: true, // 深度监视
      }
    );
    /************************ userFormData功能开始 ************************/

    /************************ categoryList功能开始 ************************/
    const categoryList = ref<CategoryList>([]); // 引用数据类型, 数组类型会推论为 never[]
    // 使用mounted生命周期函数
    onMounted(() => {
      console.log(111);

      // 模拟异步操作
      setTimeout(() => {
        categoryList.value = [
          {
            categoryId: 1,
            categoryName: "手机",
            categoryChild: [
              {
                categoryId: 2,
                categoryName: "华为",
                categoryChild: [],
              },
            ],
          },
        ];
      }, 1000);
    });

    // 1. 给元素绑定ref属性
    // 2. 定义ref数据，名称必须和绑定ref属性的值名称一致
    const ppp = ref();

    onMounted(() => {
      console.log(222);
      // 3. 获取DOM元素
      console.log(ppp.value);
    });

    onMounted(() => {
      console.log(333);
    });
    /************************ categoryList功能结束 ************************/

    /************************ count功能开始 ************************/
    // 数据
    const count = ref<number>(0); // 基本数据类型，类型可以自动推论
    // 2. 定义方法
    // 更新数据的方法
    const handleClick = () => {
      count.value++;
    };

    // 只读的计算属性
    const text = computed(() => {
      return count.value + "次";
    });

    // 可读可写的计算属性
    const xxx = computed({
      get() {}, // 读取xxx触发的函数
      set() {}, // 设置xxx触发的函数
    });

    // 监视属性
    watch(
      count,
      (newVal, oldVal) => {
        console.log("count数据变化了", newVal, oldVal);
      },
      {
        immediate: true, // 是否第一次触发
        // deep: true, // 深度监视
      }
    );

    /************************ count功能结束 ************************/

    // 返回的对象，最终会添加到组件实例对象上
    // （返回对象上的属性或方法，模板页面可以直接使用）
    return {
      count,
      setAge,
      handleClick,
      userFormData,
      text,
      ppp,
    };
  },
});
</script>

<style></style>
